﻿import kliment.controls.ControlsAbstract;
import kliment.controls.MouseGesture;
import kliment.controls.Orientation;
import kliment.controls.ScrollerBox;
import kliment.display.MCC;
import kliment.events.MouseGestureEvent;
import kliment.math.Calc;
import kliment.utils._Array;
import kliment.utils._Type;
import mx.utils.Delegate;

/**
 * ...
 * @author Kliment
 * @version 2.1.22
 */
class kliment.controls.Scroller extends ControlsAbstract {
	static private var _spaces_mcArray:Array;
	private var _intervalId:Number;
	private var _intervalIdM:Number;
	private var _intervalIdUpdate:Number;
	
	private var _seeker_step:Number = 2;
	private var _move_step:Number = .1;
	private var _delay:Number = 25;
	private var _delay_button:Number = 50;
	private var _min_height_seeker:Number = 5;
	private var _rate:Number = .4;
	private var _scroll_part:Number = .25;
	private var _stepPixels:Number;
	private var _stepInPixels:Boolean = false;
	private var _keepMouse:Boolean = false;
	private var _scaleSeeker:Boolean = false;
	private var _keyboardEnable:Boolean = true;
	private var _rounded:Boolean = true;
	private var _target_size:Number = 0;
	
	private var _orientation;
	private var _x_y_mouse:String = '_ymouse';
	
	private var _position:Number = 0;
	private var _target_mc:MovieClip;
	private var _target_mask_mc:MovieClip;
	private var _scrollerBox:ScrollerBox;
	private var _aMouseGesture:MouseGesture;
	private var _mouseGestureEnable:Boolean = true;
	
	/**
	 * 
	 * @param	target_mc MovieClip
	 * @param	mask_mc MovieClip
	 * @param	options Object {scrollerBox:ScrollerBox, rate:Number, orientation, stepPixels:Number}
	 */
	public function Scroller(target_mc:MovieClip, mask_mc:MovieClip, options:Object) {
		_target_mc = target_mc;
		_target_mask_mc = mask_mc;
		
		if (!_spaces_mcArray)
			_spaces_mcArray = new Array();
		_spaces_mcArray.push(_target_mask_mc);
		
		_aMouseGesture = new MouseGesture(_target_mask_mc || _target_mask_mc.target_mc, 300, 200);
		
		setOptions(options);
		
		_setControlActions();
		update();
	}
	
	/**
	 * 
	 * @param	options Object {scrollerBox:ScrollerBox, rate:Number, orientation:*, scaleSeeker:Boolean, stepPixels:Number, mouseGestureEnable:Boolean, keyboardEnable:Boolean, noMask:Boolean, min_height_seeker:Number}
	 */
	public function setOptions(options:Object):Void {
		_scrollerBox = options.scrollerBox;
		_scrollerBox.scroller = this;
		
		if (options.noMask == null) {
			if (_Type.isMovieClip(_target_mc) && _target_mc._parent == _target_mask_mc._parent)
				_target_mc.setMask(_target_mask_mc);
		} else {
			_target_mc.setMask(null);
		}
		
		if (options.rounded != null)
			_rounded = options.rounded;
		
		if (options.keepMouse != null)
			_keepMouse = options.keepMouse;
			
		if (options.scaleSeeker != null)
			_scaleSeeker = options.scaleSeeker;
		
		if (options.min_height_seeker != null)
			_min_height_seeker = options.min_height_seeker;
			
		if (options.keyboardEnable != null)
			_keyboardEnable = options.keyboardEnable;
			
		if (options.mouseGestureEnable != null)
			_mouseGestureEnable = options.mouseGestureEnable;
			
		if (!isNaN(options.rate)) 
			_rate = options.rate;
		
		_set_orientation(options.orientation || Orientation.VERTICAL);
		
		if (options.stepPixels != null) 
			setPixelStep(options.stepPixels);
		
		if (options.update != null) 
			update(options.update);
		else
			update();
	}
	
	public function moveUp():Void {
		if (_scrollerBox.seeker_btn && _scrollerBox.space_mc) 
			_moveSeeker(-_seeker_step);
		else 
			move(_position - _move_step);
	}
	
	public function moveDown():Void {
		if (_scrollerBox.seeker_btn && _scrollerBox.space_mc) 
			_moveSeeker(_seeker_step);
		else 
			move(_position + _move_step);
	}
	
	public function move(value:Number):Void {
		value = Math.max(0, Math.min(value, 1));
		_position = value;
	}
	
	public function showPart(startPix:Number, endPix:Number):Void {
		var partLength:Number = startPix + endPix;
		var _target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
		var _target_X_Y:Number = _getX_Y(_target_mc);
		var _target_mask_X_Y:Number = _getX_Y(_target_mask_mc);
		
		/*if (partLength > _target_mask_W_H) {
			var deltaHalf:Number = (partLength - _target_mask_W_H) * 5;
			startPix += deltaHalf;
			endPix -= deltaHalf;
		}*/
		
		var to:Number;
		
		var leftStart:Number = _target_X_Y + startPix + _target_mask_X_Y;
		if (leftStart < _target_mask_X_Y) {
			to = startPix / _content_difference_size() * 100;
		}
		
		var rihtStop:Number = _target_X_Y + endPix + _target_mask_X_Y;
		if (rihtStop > _target_mask_X_Y + _target_mask_W_H) {
			to = (_target_mask_W_H - endPix) / _content_difference_size() * 100;
		}
		
		if (!isNaN(to)) {
			positionPercent(Math.abs(to));
		}
	}
	
	public function setPixelStep(value:Number):Void {
		if (_stepPixels != value && value != 0) {
			_stepInPixels = true;
			_stepPixels = value;
		} else {
			_stepInPixels = false;
			_stepPixels = 0;
		}
		_set_move_step();
	}
	
	public function update(every_mseconds:Number, size:Number):Void {
		_update(size);
		if (every_mseconds == undefined)
			return;
		
		clearInterval(_intervalIdUpdate);
		if (every_mseconds > 0) 
			_intervalIdUpdate = setInterval(this, '_update', every_mseconds, size);
	}
	
	private function _update(size:Number):Void {
		_target_mc._width;
		_target_mc._height;
		clearInterval(_intervalIdM);
		Key.removeListener(this);
		Mouse.removeListener(this);
		
		_target_size = (size > 0 && size != undefined) ? size :_getWidth_Height(_target_mc);
		
		_set_move_step();
		if (_scrollerBox.seeker_btn) {
			_set_seeker_step();
			_calcSeekerPosition();
			_fixPointerPosition();
		}
		var _target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
		
		if (_target_size > _target_mask_W_H) {
			_scrollerBox.visible = true;
			if (_keyboardEnable)
				Key.addListener(this);
			if ((_scrollerBox.seeker_btn && _scrollerBox.space_mc) || _scrollerBox.up_btn || _scrollerBox.down_btn) {
				if (_scaleSeeker) 
					_scrollerBox.seeker_btn.height = Math.max(_min_height_seeker, _target_mask_W_H / _target_size * _scrollerBox.space_mc._height);
				_aMouseGesture.enable = _mouseGestureEnable && enable;
				_aMouseGesture.minSize = _target_mask_W_H * _scroll_part;
				Mouse.addListener(this);
				_intervalIdM = setInterval(this, '_moveContent', _delay);
			} else {
				_aMouseGesture.enable = false;
				_intervalIdM = setInterval(this, '_flyContent', _delay);
			}
			
			var min_position:Number = _getX_Y(_target_mask_mc) + _content_difference_size();
			if (_getX_Y(_target_mc) < min_position) {
				_position = min_position;
				_setX_Y(_target_mc, _position);
			}
		} else {
			_aMouseGesture.enable = false;
			_scrollerBox.visible = false;
			_setX_Y(_target_mc, _getX_Y(_target_mask_mc))
		}
		if (!_enable) {
			clearInterval(_intervalIdM);
			Mouse.removeListener(this);
			Key.removeListener(this);
		}
	}
	
	public function positionPercent(value:Number):Void {
		value = Calc.inInterval(0, 100, value) / 100 || 0;
		if (!_scrollerBox.seeker_btn) {
			_position = - (value * (_target_size - _getWidth_Height(_target_mask_mc)));
		} else {
			_moveSeeker(value * (_scrollerBox.space_mc._height - _scrollerBox.seeker_btn.height))
		}
	}
	
	private function _getX_Y(target:Object):Number {
		var preffix:String = _getTargetPreffix(target);
		if (_orientation == Orientation.HORIZONTAL)
			return target[preffix + 'x'];
		return target[preffix + 'y'];
	}
	
	private function _setX_Y(target:Object, value:Number):Void {
		var preffix:String = _getTargetPreffix(target);
		if (_orientation == Orientation.HORIZONTAL)
			target[preffix + 'x'] = value;
		else 
			target[preffix + 'y'] = value;
	}
	
	private function _getWidth_Height(target:Object):Number {
		var preffix:String = _getTargetPreffix(target);
		if (_orientation == Orientation.HORIZONTAL)
			return target[preffix + 'width'];
		return target[preffix + 'height'];
	}
	
	private function _setWidth_Height(target:Object, value:Number):Void {
		var preffix:String = _getTargetPreffix(target);
		if (_orientation == Orientation.HORIZONTAL)
			target[preffix + 'width'] = value;
		else 
			target[preffix + 'height'] = value;
	}
	
	private function _getTargetPreffix(target:Object):String {
		if (target instanceof MovieClip)
			return '_';
		return '';
	}
	
	private function _setControlActions():Void {
		var this_c:Scroller = this;
		if (_scrollerBox.seeker_btn && _scrollerBox.space_mc) {			
			var seekerListener:Object = new Object();
			seekerListener.onPress = function():Void {
				clearInterval(this_c._intervalId);
				var space:MovieClip = this_c._scrollerBox.space_mc;
				this_c._scrollerBox.seeker_btn.startDrag(false, space._x, space._y, space._x, space._height - this_c._scrollerBox.seeker_btn.height + space._y);
				this_c._intervalId = setInterval(this_c, '_calcSeekerPosition', this_c._delay);
			}
			seekerListener.onRelease = seekerListener.onReleaseOutside = function():Void {
				this_c._scrollerBox.seeker_btn.stopDrag();
				clearInterval(this_c._intervalId);
			}
			_scrollerBox.seeker_btn.addListener(seekerListener);
			_scrollerBox.space_mc.onPress = function():Void {
				this_c._flySeeker();
				clearInterval(this_c._intervalId);
				this_c._intervalId = setInterval(this_c, '_flySeeker', this_c._delay_button);
			}
			_scrollerBox.space_mc.onRelease = _scrollerBox.space_mc.onReleaseOutside = function():Void {
				clearInterval(this_c._intervalId);
			}
			
			_scrollerBox.space_mc.useHandCursor = false;
		}
		if (_scrollerBox.up_btn) {
			var upListener:Object = new Object();
			upListener.onPress = function():Void {
				this_c.moveUp();
				clearInterval(this_c._intervalId);
				this_c._intervalId = setInterval(this_c, '_runInterval', this_c._delay_button * 3, 'moveUp');
			}
			upListener.onRelease = upListener.onReleaseOutside = function():Void {
				clearInterval(this_c._intervalId);
			}
			_scrollerBox.up_btn.addListener(upListener);
		}
		
		if (_scrollerBox.down_btn) {
			var downListener:Object = new Object();
			downListener.onPress = function():Void {
				this_c.moveDown();
				clearInterval(this_c._intervalId);
				this_c._intervalId = setInterval(this_c, '_runInterval', this_c._delay_button * 3, 'moveDown');
			}
			downListener.onRelease = downListener.onReleaseOutside = function():Void {
				clearInterval(this_c._intervalId);
			}
			_scrollerBox.down_btn.addListener(downListener);
		}
	}
	
	private function _runInterval(fun:String):Void {
		clearInterval(_intervalId);
		_intervalId = setInterval(this, fun, _delay_button);
	}
	
	private function _set_orientation(orientation):Void {
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
		
		if (orientation != _orientation) {
			switch (orientation) {
				case Orientation.HORIZONTAL: 
					_x_y_mouse = '_xmouse';
					_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_LEFT, _mouseGestureHandler);
					_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_RIGHT, _mouseGestureHandler);
				break;
				case Orientation.VERTICAL: default: 
					_x_y_mouse = '_ymouse';
					_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
					_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
				break; 
				
				case Orientation.FULL:  
					trace('Orientation.' + Orientation.FULL + " don't use");
				break; 
			}
			_orientation = orientation;
		}
	}
	
	private function _set_move_step():Void {
		if (!_stepInPixels) {
			var m:Number = _getWidth_Height(_target_mask_mc) * _scroll_part;
			_move_step = m / (_target_size - _getWidth_Height(_target_mask_mc));
		} else {
			//_move_step = 
		}
	}
	
	private function _set_seeker_step():Void {
		_seeker_step = (_scrollerBox.space_mc._height - _scrollerBox.seeker_btn.height) * _move_step;
	}
	
	private function onMouseWheel(delta:Number):Void {
		if (!_inSpace()) 
			return;
		if (delta > 0) 
			moveUp();
		else 
			moveDown();
	}
	
	private function onKeyDown():Void {
		if (!_inSpace() && !_keyboardEnable) 
			return;
		
		var keyCode:Number = Key.getCode();
		switch(keyCode) {
			case Key.UP:
				if (_orientation == Orientation.VERTICAL)
					moveUp();
			break;
			
			case Key.DOWN:
				if (_orientation == Orientation.VERTICAL)
					moveDown();
			break;
			
			case Key.LEFT :
				if (_orientation == Orientation.HORIZONTAL)
					moveUp();
			break;
			
			case Key.RIGHT:
				if (_orientation == Orientation.HORIZONTAL)
					moveDown();
			break;
		}
	}
	
	private function _mouseGestureHandler(event:MouseGestureEvent):Void {
		if (!_enable) 
			return;
		var speed:Number = event.distance / _aMouseGesture.delay;
		switch(event.type) {
			case MouseGestureEvent.SLIDE_DOWN: 
			case MouseGestureEvent.SLIDE_RIGHT: 
				moveUp();//move(_position - _move_step);
			break;
			
			case MouseGestureEvent.SLIDE_UP:
			case MouseGestureEvent.SLIDE_LEFT: 
				moveDown()//move(_position + _move_step);
			break;
		}
	}
	
	private function _flyContent():Void {
		var target_X_Y:Number = _getX_Y(_target_mc);
		if (_inSpace()) {
			var parent_mc:MovieClip = _target_mask_mc._parent;
			var target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
			var start:Number = _getX_Y(_target_mask_mc);
			var end:Number = _target_size - target_mask_W_H - start;
			
			if (_keepMouse) 
				_position = -((parent_mc[_x_y_mouse] - start) / target_mask_W_H * (_target_size - target_mask_W_H)) + start;
			else 
				_position = target_X_Y - (parent_mc[_x_y_mouse] - (target_mask_W_H * .5 + start)) * (_move_step * _rate);
			
			_position = Calc.inInterval( -end, start, _position);
			
			if (_rounded)
				_position = Math.round(_position);
		}
		
		_setX_Y(_target_mc, Calc.deltaRatioResult(_position, target_X_Y, _rate));
	}
	
	private function _inSpace():Boolean {
		/*var isHits:Array = new Array() 
		var i:Number = _spaces_mcArray.length;
		var result:Boolean = false;
		while (i--) {
			var item = _spaces_mcArray[i]
			if (MCC.hitTestBoundsMouse(item))
				isHits.push(item);
		}
		trace(isHits);
		i = _spaces_mcArray.length
		while (i--) {
			
		}
		return result;*/
		return MCC.hitTestBoundsMouse(_target_mask_mc);
	}
	
	private function _flySeeker():Void {
		var step:Number = (_scrollerBox.seeker_btn.target_mc._parent._ymouse - _scrollerBox.seeker_btn.y - _scrollerBox.seeker_btn.height * .5) * (_rate * .5 );
		_moveSeeker(step);
	}
	
	private function _moveSeeker(delta:Number):Void {
		_scrollerBox.seeker_btn.y += delta;
		_fixPointerPosition();
		_calcSeekerPosition();
	}
	
	private function _calcSeekerPosition():Void {
		//_getWidth_Height(_target_mc);
		var contentSize:Number = _content_difference_size();
		
		_position = (_scrollerBox.seeker_btn.y - _scrollerBox.space_mc._y) / (_scrollerBox.space_mc._height - _scrollerBox.seeker_btn.height);
	}
	
	private function _fixPointerPosition():Void {
		var maxDown:Number = _scrollerBox.space_mc._height + _scrollerBox.space_mc._y - _scrollerBox.seeker_btn.height;
		/*_scrollerBox.seeker_btn.y = (_scrollerBox.seeker_btn.y < _scrollerBox.space_mc._y)
						?_scrollerBox.space_mc._y
						:((_scrollerBox.seeker_btn.y > maxDown)
							?maxDown
							:_scrollerBox.seeker_btn.y);*/
		_scrollerBox.seeker_btn.y = Calc.inInterval(_scrollerBox.space_mc._y, maxDown, _scrollerBox.seeker_btn.y);
	}
	
	private function _moveContent():Void {
		var target_X_Y:Number = _getX_Y(_target_mc);
		var contentSize:Number = _content_difference_size();
		var position:Number = contentSize - (contentSize * (1 - _position));
		if (_rounded)
			position = Math.round(position);
		_setX_Y(_target_mc, target_X_Y + (position - target_X_Y  + _getX_Y(_target_mask_mc)) * _rate);
	}
	
	private function _content_difference_size():Number {
		return _getWidth_Height(_target_mask_mc) - _target_size;
	}
	
	private function _scroll_difference_size():Number {
		return _scrollerBox.space_mc._height - _scrollerBox.seeker_btn.height;
	}
	
	public function set enable(value:Boolean):Void {
		if (value != _enable) {
			super.enable = value;
			update();
		}
	}
	
	public function get mouseGesture():MouseGesture { return _aMouseGesture; }
	
	public function remove():Void {
		_Array.remove(_target_mask_mc, _spaces_mcArray);
		clearInterval(_intervalId);
		clearInterval(_intervalIdM);
		clearInterval(_intervalIdUpdate);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
		_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
		_aMouseGesture.remove();
		Mouse.removeListener(this);
		Key.removeListener(this);
		delete this;
	}
}